//
//  JMDownloader.swift
//  TQMedia
//
//  Created by XieLB on 2018/9/27.
//  Copyright © 2018年 XieLB. All rights reserved.
//

import UIKit
import Alamofire

// 视频文件夹
let kVideoDirectory: String = "Video"
let kCachesDirectoryURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
let kVideoDireURL: URL = kCachesDirectoryURL.appendingPathComponent(kVideoDirectory)

enum DownloaderState: String {
    case unknown    = "unknown"
    case loading    = "loading"
    case pause      = "pause"
    case succeed    = "succeed"
    case failed     = "failed"
    case play       = "play"
}

class JMDownloader: NSObject {

    // 用于记录是否完成
    var downloaderState: DownloaderState = .unknown
    
    // 用于暂停下载时候，保存已经下载的部分。等下次可以接着下载
    var resumeData: Data?
    
    // 下载请求对象
    var request: DownloadRequest?
    
//    // 记录当前下载进度
//    var progress: Progress = Progress()

    // 用于存入数据库
    var downloadParam = Dictionary<String, Any>()
    
    // 用于区分 刷新取消下载
    var isResfreshCancleDownload: Bool = false
    
    var isAlreadyloadingCount: Int = 0
    
    var isShouldDeleteDownLoad: ((JMDownloader)->())?
}

// MARK:-  第一次, 用Url下载
extension JMDownloader {
    
    func startDownLoad(_ url: String, _ completed:@escaping (Dictionary<String, Any>)->()) {
        
        // 1. 保存路径
        // 1.1 创建文件路径
        //        let prifix: String = url.hasSuffix("mp4")
        // 文件
        //        注意************************************请不要改动，报错在 .md5 的方法前 + public 即可******************
        let fileName = url.md5 + ".mp4"
        let fileURL = kVideoDireURL.appendingPathComponent(fileName)
        debugPrint(fileURL)
        
        // 1.2 设置 destination
        let destination: DownloadRequest.DownloadFileDestination? = { _, _ in
            return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
        }
        
        // 2. 检查本地文件 resumeData
        do {
            // 2.1 取出数据
            resumeData =  try Data(contentsOf: fileURL)
            print("本地读取成功")
        } catch {
            print("本地不存在？ error: ", error)
        }
        
        // 设置Item url 、name
        downloadParam["url"] = url
        downloadParam["fileName"] = fileName
        downloadParam["id"] = url
        downloadParam["dataTime"] = "\(Date())"
        print("\(#file)的\(#function)方法在第\(#line)行：destination=\(String(describing: destination)) url=\(url)")
        // 3. 设置 request
        if let resumeData = resumeData {
            print("继续下载")
            // 续传
            request = Alamofire.download(resumingWith: resumeData, to: destination)
        } else {
            // 设置url 及 存储位置
            request = Alamofire.download(url, to: destination)
        }
        // 4. 开始下载
        fetchData(completed)
    }
}

// MARK:-  已下载过的，用item下载
extension JMDownloader {
    
    func startDownLoadWithItem(_ item: Dictionary<String, Any>, _ completed:@escaping (Dictionary<String, Any>)->()) {
        
        // 赋值 item
        downloadParam = item
        
        // 1. 保存路径
        // 1.1 创建文件路径
        //        let prifix: String = url.hasSuffix("mp4")
        // 文件
        guard let fileName = item["fileName"] as? String else {
            return
        }
        
        let fileURL = kVideoDireURL.appendingPathComponent(fileName)
        debugPrint(fileURL)
        
        // 1.2 设置 destination
        let destination: DownloadRequest.DownloadFileDestination! = { _, _ in
            return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
        }
        
        // 2. 检查本地文件 resumeData
        do {
            // 2.1 取出数据
            resumeData =  try Data(contentsOf: fileURL)
            print("本地读取成功")
        } catch {
            print("本地不存在？ error: ", error)
        }
        
        // 3. 设置 request
        if let resumeData = resumeData {
            print("继续下载")
            // 续传
            request = Alamofire.download(resumingWith: resumeData, to: destination)
            
        } else {
            print("本地读取数据失败，重新下载")
            // 设置url 及 存储位置
            request = Alamofire.download((item["url"] as? String) ?? "", to: destination)
        }
        
        // 4. 开始下载
        fetchData(completed)
    }
}

extension JMDownloader {
    
    func fetchData(_ completed:@escaping (Dictionary<String, Any>)->()) {
        
        // 开始下载
        
        // 设置添加线程
        let utilityQueue = DispatchQueue.global(qos: .utility)
        
        // 1. 下载进度
        request?.downloadProgress(queue: utilityQueue, closure: { [weak self] progress in
            
            // 设置Item loadSize 、 allSize
            let totalM = progress.totalUnitCount/1024/1024
            let completedM = progress.completedUnitCount/1024/1024
            let progressValue = progress.fractionCompleted
            self?.downloadParam["allSize"] = String(format: "%dM", totalM)
            self?.downloadParam["loadSize"] = String(format: "%2dM", completedM)
            self?.downloadParam["progress"] = String(format: "%.2f", progressValue)
            self?.downloadParam["state"] = "\(DownloaderState.loading.rawValue)"    // 重新下载时.loading
            
            // 插入数据库（第一次插入）
            if self?.downloaderState == .unknown {
                // 修改状态（不能放到插入回调中）
                self?.downloaderState = .loading
                
//                DownloadHandle.shareInstance().insert(vModel: (self?.downloadItem)!, { (isSeccessful) in
//                    debugPrint("\(isSeccessful ? "成功" : "失败")" + "插入数据库")
//                })
                
            } else {
                // 更新 数据库
                if (self?.downloadParam["id"] as? String) != "" {
//                    DownloadHandle.shareInstance().updateRow((self?.downloadItem.id)!, vModel:(self?.downloadItem)!) { isUpdateSuccesssful in
//                        debugPrint("\(isUpdateSuccesssful ? "成功" : "失败")" + "更新数据库")
//                    }
                }
            }
            // 回调 item
            completed((self?.downloadParam)!)
        })
        
        // 2. 下载完成
        request?.responseData(completionHandler: { [weak self] response in
            
            switch response.result {
            case .success://(let data):
                print("下载成功，设置了 destination， 自动存入本地")
                self?.downloaderState = .succeed
                self?.downloadParam["state"] = "\(DownloaderState.succeed.rawValue)"
                
            case .failure(let error):
                let nsError = error as NSError
                print("下载failure状态：", nsError.code, error.localizedDescription)
                
                if nsError.code == -999 { // error.localizedDescription == "cancelled" || error.localizedDescription == "已取消"
                    // 区分刷新导致的failure 设置为 loading状态
                    if let isResfreshCancle = self?.isResfreshCancleDownload {
                        if isResfreshCancle {
                            self?.downloaderState = .loading
                            self?.downloadParam["state"] = "\(DownloaderState.loading.rawValue)"
                            
                        } else {
                            self?.downloaderState = .pause
                            self?.downloadParam["state"] = "\(DownloaderState.pause.rawValue)"
                        }
                    } else {
                        self?.downloaderState = .pause
                        self?.downloadParam["state"] = "\(DownloaderState.pause.rawValue)"
                    }
                    
                }
                else if nsError.code == -1002 {// 已下载完成的再次下载了  isAlreadyloading
                    
                    var length: Int64 = 0
                    if self?.resumeData != nil {
                        length = Int64((self?.resumeData!.count)!/1024/1024)
                    }
                    let allSize = String(format: "%dM", length)
                    
                    if self?.resumeData != nil &&
                        (self?.downloadParam["allSize"]as? String) != "" &&
                        (self?.downloadParam["allSize"]as? String) == allSize
                    {
                        self?.downloaderState = .succeed
                        self?.downloadParam["state"] = "\(DownloaderState.succeed.rawValue)"
                        print("下载失败：已下载完成")
                    } else {
                        self?.downloaderState = .failed
                        self?.downloadParam["state"] = "\(DownloaderState.failed.rawValue)"
                        print("下载失败：未下载完成")
                    }
                }
                else if nsError.code == -1009 {// 无网络
                    self?.downloaderState = .failed
                    self?.downloadParam["state"] = "\(DownloaderState.failed.rawValue)"
                }
                
                // 保存，用做续传
                do {
                    // 写入本地
                    let fileURL = kVideoDireURL.appendingPathComponent(self?.downloadParam["fileName"] as! String)
                    try response.resumeData?.write(to: fileURL, options: Data.WritingOptions.atomic)
                    print("暂停时，数据写入本地成功！")
                    
                } catch {
                    // 本地失败写入内存
                    self?.resumeData = response.resumeData
                    print("暂停时数据写入本地失败！")
                }
            }// end -> switch
            
            
            // 更新 数据库
            if (self?.downloadParam["id"] as? String) != "" {
//                DownloadHandle.shareInstance().updateRow((self?.downloadItem.id)!, vModel:(self?.downloadItem)!) { isUpdateSuccesssful in
//                    debugPrint("\(isUpdateSuccesssful ? "成功" : "失败")" + "更新数据库")
//                }
            }
            
            // 回调
            completed((self?.downloadParam)!)
        })
    }
    
    // 暂停下载
    //    func pauseDownload() {
    //        request?.suspend() 不能用做暂停
    //    }
    
    // 停止下载
    func stopDownload() {
        request?.cancel()
    }
    
}


extension String {
    public var md5: String {
//        guard let str = cString(using: .utf8) else { return self }
//        let strLen = CC_LONG(lengthOfBytes(using: .utf8))
//        let digestLen = Int(CC_MD5_DIGEST_LENGTH)
//        let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)
//        CC_MD5(str, strLen, result)
//
//        var hash = String()
//        for i in 0..<digestLen {
//            hash += String(format: "%02x", result[i])
//        }
//
//        result.deallocate(capacity: digestLen)
//
//        return hash.uppercased()
//
        return self
    }
}
